Load data


• Neocortex_v3.RData Not necessary if just plotting on already calculated PCA, UMAP, clusters.

# Full dataset:
load("../data/cfc4b2_neocortex_v3.RData")

# 40k random subset (toy dataset)
# ncx.40k <- read_rds("constellation_plots/ncx_v3_40k.rds")

# 100k random subset of excitatory neuron lineage only.
ncx.exn.100k <- read_rds("../data/exn_lineage/toy/ncx.v3.exn.sub_100k.rds")

• Cluster / marker tables.

ncx.clusters <- read_delim("../tbls/83d19_Neocortex_allindividuals_combinedclusters_v1.txt",
                              "\t", escape_double = FALSE, trim_ws = TRUE)

ncx.markers <- read_delim("../tbls/8f0fc_Neocortex_subset1_clustermarkers_combo2.txt", 
                  "\t", escape_double = FALSE, trim_ws = TRUE)

scrattch.hicat steps to build constellation plots.

1. Define cells to build the plot from.

[All cells: 404.2K]

a. All clusters / cell types:

Keep only cells with combo2 $combined.cluster.2 annotation.

b. Excitatory neuron lineage only:

Keep only cells with combo2 $combined.cluster.2 annotation AND whose $combined.cluster.2 annotation belongs to excitatory neuronal lineage classes.

b.1. Cells in exn linage 100k cell toy object:

2. Build dataframes for constellation plots.

1. cl

2. rd.dat

3. cl.df

4. rd.cl.center Find cluster centers from UMAP coordinates

5. cl.center.df

Join cl.df and rd.cl.center into cl.center.df for input into get_KNN_graph.

6. knn.cl


Attaching package: ‘matrixStats’

The following object is masked from ‘package:dplyr’:

    count

3. Make constellation plot


2. Find DE genes between connected clusters.


Neuron_3 cells with nearest neighbors in Neuron_8 cell.cl.counts is a matrix of all cells and their counts of nearest neighbors in each cluster.

Find cells in cluster_a with bearest neighbors in cluster_b

nn.counts <- x[[cluster_b_col]]
length(x$cell.name)
[1] 10663
sum(nn.counts > 0)
[1] 6103
sum(nn.counts == 0)
[1] 4560
(median.nnCounts <- median(nn.counts[nn.counts > 0]))
[1] 5

Distribution of neighbor counts in cluster_b for cells in a given cluster_a. Use this to find the point at which cells will be split into comparison groups.

Compare cells above and below the median count of cluster_b neighbors.

cells <- list()
cells$above.median <- x %>% filter(get(cluster_b_col) > median) %>% pull(cell.name)
cells$below.median <- x %>% filter(get(cluster_b_col) < median)  %>% pull(cell.name)

s.obj <- SetIdent(s.obj, cells = cells$above.median, value = 'nn_ct_above_med') %>% 
          SetIdent(cells = cells$below.median, value = 'nn_ct_below_med')

table(s.obj@active.ident) %>% as.data.frame.matrix()
markers <- list()
markers$nn_above_med <- FindMarkers(s.obj, slot = "scale.data", 
                                  # cells.1 = cells$above.median, cells.2 = cells$below.median,
                                  ident.1 = "nn_above_med", ident.2 = "nn_below_med", 
                                  logfc.threshold = 0)

Calculate enrichment ratio, filter genes w. adj p-value < 0.05, sort table. Positive values indicate that the feature is more highly expressed in the first group.


markers.tmp <- markers$nn_above_med %>% rownames_to_column("gene") %>%
  mutate(enrich.ratio = pct.1 / pct.2,
         gene.score = avg_diff * enrich.ratio,
         across(.cols = where(is.numeric), .fns = round, digits = 5)
  ) %>%
  filter(p_val_adj <= 0.05) %>% 
  select(gene, pct.1, pct.2, enrich.ratio, avg_diff, gene.score, p_val_adj) %>%
  arrange(desc(gene.score))

write_tsv(markers.tmp, "../out/DEgenes_neuron3_vs_neuron8.tsv")

Make heatmap:

Extra functions

LS0tCnRpdGxlOiAiQ29uc3RlbGxhdGlvbiBQbG90czogTmVvY29ydGV4LCAybmQgVHJpbWVzdGVyIiAKc3VidGl0bGU6ICJBbGwgU2FtcGxlcywgQWxsIGNlbGwgdHlwZXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04LCBmaWcucGF0aCA9ICdGaWdzLycsCiAgICAgICAgICAgICAgICAgICAgICBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpzb3VyY2Vfcm1kKCJzY3JhdHRjaC5oaWNhdF9meG5zLlJtZCIpCmBgYAoKIyBMb2FkIGRhdGEKKioqIAoK4oCiIE5lb2NvcnRleF92My5SRGF0YQpOb3QgbmVjZXNzYXJ5IGlmIGp1c3QgcGxvdHRpbmcgb24gYWxyZWFkeSBjYWxjdWxhdGVkIFBDQSwgVU1BUCwgY2x1c3RlcnMuCmBgYHtyIGxvYWQtbmVvY29ydGV4LCBldmFsID0gRkFMU0UsIGVjaG89VFJVRX0KIyBGdWxsIGRhdGFzZXQ6CmxvYWQoIi4uL2RhdGEvY2ZjNGIyX25lb2NvcnRleF92My5SRGF0YSIpCgojIDQwayByYW5kb20gc3Vic2V0ICh0b3kgZGF0YXNldCkKIyBuY3guNDBrIDwtIHJlYWRfcmRzKCJjb25zdGVsbGF0aW9uX3Bsb3RzL25jeF92M180MGsucmRzIikKCiMgMTAwayByYW5kb20gc3Vic2V0IG9mIGV4Y2l0YXRvcnkgbmV1cm9uIGxpbmVhZ2Ugb25seS4KbmN4LmV4bi4xMDBrIDwtIHJlYWRfcmRzKCIuLi9kYXRhL2V4bl9saW5lYWdlL3RveS9uY3gudjMuZXhuLnN1Yl8xMDBrLnJkcyIpCmBgYAoK4oCiIENsdXN0ZXIgLyBtYXJrZXIgdGFibGVzLgpgYGB7ciBsb2FkLXRibHMsICBlY2hvPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9Cm5jeC5jbHVzdGVycyA8LSByZWFkX2RlbGltKCIuLi90YmxzLzgzZDE5X05lb2NvcnRleF9hbGxpbmRpdmlkdWFsc19jb21iaW5lZGNsdXN0ZXJzX3YxLnR4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgdHJpbV93cyA9IFRSVUUpCgpuY3gubWFya2VycyA8LSByZWFkX2RlbGltKCIuLi90YmxzLzhmMGZjX05lb2NvcnRleF9zdWJzZXQxX2NsdXN0ZXJtYXJrZXJzX2NvbWJvMi50eHQiLCAKICAgICAgICAgICAgICAgICAgIlx0IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCB0cmltX3dzID0gVFJVRSkKYGBgCgoqKioKCiMgYHNjcmF0dGNoLmhpY2F0YCBzdGVwcyB0byBidWlsZCBjb25zdGVsbGF0aW9uIHBsb3RzLgoKIyMgMS4gRGVmaW5lIGNlbGxzIHRvIGJ1aWxkIHRoZSBwbG90IGZyb20uCltBbGwgY2VsbHM6IDQwNC4yS10KICAKIyMgYS4gQWxsIGNsdXN0ZXJzIC8gY2VsbCB0eXBlczoKS2VlcCBvbmx5IGNlbGxzIHdpdGggKl9jb21ibzJfKiBgJGNvbWJpbmVkLmNsdXN0ZXIuMmAgYW5ub3RhdGlvbi4KYGBge3IgZXZhbD1GQUxTRX0KCmNlbGxzLmNsLmRmIDwtICBuY3guY2x1c3RlcnMgJT4lIGZpbHRlcihzdHJfZGV0ZWN0KGNvbWJpbmVkLmNsdXN0ZXIuMiwgImNvbWJvMiIpKQoKcy5vYmogPC0gTmVvY29ydGV4ICU+JSBzdWJzZXRTZXVyYXQoY2VsbHMua2VlcCA9IGNlbGxzLmNsLmRmJGNlbGwubmFtZSkKIyAzNDhLIGNlbGxzCgojIFNhbml0eSBjaGVjawpjZWxscy5jbC5kZiRjZWxsLm5hbWUgJT4lIGlkZW50aWNhbChzLm9iakBtZXRhLmRhdGEgJT4lIHJvd25hbWVzKQpbVFJVRV0KYGBgCgojIyMgYi4gRXhjaXRhdG9yeSBuZXVyb24gbGluZWFnZSBvbmx5OgoKS2VlcCBvbmx5IGNlbGxzIHdpdGggKl9jb21ibzJfKiBgJGNvbWJpbmVkLmNsdXN0ZXIuMmAgYW5ub3RhdGlvbiBBTkQKd2hvc2UgYCRjb21iaW5lZC5jbHVzdGVyLjJgIGFubm90YXRpb24gYmVsb25ncyB0byBleGNpdGF0b3J5IG5ldXJvbmFsIGxpbmVhZ2UgY2xhc3Nlcy4KYGBge3IgZXZhbD1GQUxTRX0KY2VsbHMuY2wuZGYgPC0gbmN4LmNsdXN0ZXJzLmV4biA8LSAKICAgICAgICAgICAgICAgICAgbmN4LmNsdXN0ZXJzICU+JSAKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihzdHJfZGV0ZWN0KGNvbWJpbmVkLmNsdXN0ZXIuMiwgImNvbWJvMiIpICkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoc3RyX2RldGVjdChjb21iaW5lZC5jbHVzdGVyLjIsICJOZXVyb258Q1J8RGl2aWRpbmd8Ukd8SVBDfE9QQyIpICkKCgpzLm9iaiA8LSBOZW9jb3J0ZXggJT4lIHN1YnNldFNldXJhdChjZWxscy5rZWVwID0gY2VsbHMuY2wuZGYkY2VsbC5uYW1lKQoKIyBSZW1vdmUgQGNvdW50cyBzbG90ICg1R0IpCnMub2JqQGFzc2F5cyRSTkFAY291bnRzIDwtIG1hdHJpeChjKDAsMCkpCiMgMjcxLjRLIGNlbGxzIGluIGV4Y2l0YXRvcnkgbGluZWFnZS4KCiMgU2FuaXR5IGNoZWNrOgpjZWxscy5jbC5kZiRjZWxsLm5hbWUgJT4lIGlkZW50aWNhbChzLm9iakBtZXRhLmRhdGEgJT4lIHJvd25hbWVzKQojIFtUUlVFXQoKcy5vYmpAbWV0YS5kYXRhIDwtIGxlZnRfam9pbih4ID0gcy5vYmpAbWV0YS5kYXRhICU+JSBzZWxlY3QoLW9yaWcuaWRlbnQpICU+JSByb3duYW1lc190b19jb2x1bW4oImNlbGwubmFtZSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGNlbGxzLmNsLmRmWyAsIGMoImNlbGwubmFtZSIsICJjb21iaW5lZC5jbHVzdGVyLjIiKV0pICU+JSAKICAgICAgICAgICAgICAgICAgICAgIHNldF9yb3duYW1lcyguJGNlbGwubmFtZSkKCndyaXRlX3JkcyhzLm9iaiwgIi4uL2RhdGEvZXhuX2xpbmVhZ2UvbmVvY29ydGV4X2V4bl9saW5lYWdlXzI3MWsucmRzIiwgY29tcHJlc3MgPSAiZ3oiKQpgYGAKCiMjIyBiLjEuIENlbGxzIGluIGV4biBsaW5hZ2UgMTAwayBjZWxsIHRveSBvYmplY3Q6CmBgYHtyfQojIERlZmluZSB3aGljaCBvYmplY3QgdGhlIGNvbnN0ZWxsYXRpb24gcGxvdCBpcyBiYXNlZCBvbjoKcy5vYmogPC0gbmN4LmV4bi4xMDBrCgpjZWxscy5jbC5kZiA8LSBuY3guY2x1c3RlcnMgJT4lIAogICAgICAgICAgICAgICAgICBmaWx0ZXIoY2VsbC5uYW1lICVpbiUgcm93bmFtZXMocy5vYmpAbWV0YS5kYXRhKSApCmBgYAoKCiMjIDIuIEJ1aWxkIGRhdGFmcmFtZXMgZm9yIGNvbnN0ZWxsYXRpb24gcGxvdHMuCgojIyMgMS4gYGNsYCAKYGBge3J9CmNsIDwtIGNlbGxzLmNsLmRmJGNvbWJpbmVkLmNsdXN0ZXIuMiAlPiUgYXMuZmFjdG9yICU+JSAKICAgICAgICAgIHNldF9uYW1lcyhjZWxscy5jbC5kZiRjZWxsLm5hbWUpCiMgMTI4IGNsdXN0ZXJzIGluIGFsbCBfY29tYm8yXyBjZWxscwojIDc3IGNsdXN0ZXJzIGluIGFsbCBfY29tYm8yXyAmIEV4TiBsaW5lYWdlIGNlbGxzLgpgYGAKCiMjIyAyLiBgcmQuZGF0YApgYGB7cn0KcmQuZGF0IDwtIGxpc3QodW1hcCA9IHMub2JqQHJlZHVjdGlvbnMkdW1hcEBjZWxsLmVtYmVkZGluZ3MsCiAgICAgICAgICAgICAgIHBjYSA9IHMub2JqQHJlZHVjdGlvbnMkcGNhQGNlbGwuZW1iZWRkaW5ncykKCiMgU3Vic2V0IFVNQVAgYW5kIFBDQSBjZWxsIGVtYmVkZGluZ3M6IGtlZXAgb25seSBjZWxscyBpbiBuY3guY2x1c3RlcnMuCiMgKEV4Y2x1ZGUgY2VsbHMgaW4gY2x1c3RlciAwIGFuZCBrZWVwIG9ubHkgY2VsbHMgd2l0aCAiY29tYm8yIiBpbiBjb21iaW5lZCBjbHVzdGVyIDIgbmFtZS4pCiMgcmQuZGF0ICU8PiUgbGFwcGx5KGZ1bmN0aW9uKHgpIHhbbmFtZXMoY2wpLCBdKQpgYGAKCiMjIyAzLiBgY2wuZGZgCmBgYHtyfQpjbC5kZiA8LSBnZXRfY2xfZGYoY2wpCgpjbC5kZiRjbGFkZSA8LSBzdHJfc3BsaXRfZml4ZWQoY2wuZGYkY2x1c3Rlcl9sYWJlbCwgIl8iLCAyKVsgLDFdICU+JSB0b2xvd2VyIAojIEFkZCBjbGFkZV9pZCwgY2xhZGVfY29sb3IgdG8gY2wuZGYKY2xhZGUuY29scyA8LSBkYXRhLmZyYW1lKCMgY2xhZGUgPSB1bmlxdWUoY2wuZGYkY2xhZGUpLAogICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbG9yID0gYygiY3IiPSAiZGFya2dyZXkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkaXZpZGluZyIgPSAiZGFya2toYWtpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmV1cm9uIiA9ICJkZWVwc2t5Ymx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImludGVuZXVyb24iID0gImRlZXBwaW5rMiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImlwYyIgPSAiYnJvd240IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWljcm9nbGlhIiA9ICJkYXJrb3JjaGlkMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BjIiA9ICJjYWRldGJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvdGhlciIgPSAiZGFya3NsYXRlYmx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJnIiA9ICJkYXJrb3JhbmdlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFzY3VsYXIiID0gImJsYW5jaGVkYWxtb25kIikKICAgICAgICAgICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjbGFkZSIpCgpjbC5kZiAlPD4lIGxlZnRfam9pbihjbGFkZS5jb2xzKQpybShjbGFkZS5jb2xzKQoKIyBjZWxscy5jbC5kZjogQWRkIGNsdXN0ZXJfaWQgY29sdW1uIGZyb20gY2wuZGY7IHJlbW92ZSB1bnVzZWQgY29sdW1ucy4gCmNlbGxzLmNsLmRmIDwtIGxlZnRfam9pbihjZWxscy5jbC5kZiAlPiUgc2VsZWN0KGNlbGwubmFtZSwgY2VsbC50eXBlLCBjb21iaW5lZC5jbHVzdGVyLjIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjbC5kZiAlPiUgc2VsZWN0KGNsdXN0ZXJfbGFiZWwsIGNsdXN0ZXJfaWQpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJjb21iaW5lZC5jbHVzdGVyLjIiID0gImNsdXN0ZXJfbGFiZWwiKQogICAgICAgICAgICAgICAgKSAlPiUgbXV0YXRlKGNsdXN0ZXJfaWQgPSBhcy5mYWN0b3IoY2x1c3Rlcl9pZCkpCmBgYAoKIyMjIDQuIGByZC5jbC5jZW50ZXJgIEZpbmQgY2x1c3RlciBjZW50ZXJzIGZyb20gVU1BUCBjb29yZGluYXRlcwpgYGB7cn0KcmQuY2wuY2VudGVyIDwtIGdldF9SRF9jbF9jZW50ZXIocmQuZGF0JHVtYXAsIGNsKSAKCnJkLmNsLmNlbnRlciAlPD4lIAogIGFzLmRhdGEuZnJhbWUgJT4lIAogIHNldF9uYW1lcyhjKCJ4IiwgInkiKSkgJT4lCiAgYWRkX2NvbHVtbihjbCA9IDE6bnJvdyhyZC5jbC5jZW50ZXIpLCAuYmVmb3JlID0gIngiKSAlPiUKICAjIGFkZF9jb2x1bW4gcHJlc2VydmVzIHJvd25hbWVzLgogICMgYnV0IG1vdmluZyByb3duYW1lcyB0byBjb2x1bW4gY2x1c3Rlcl9sYWJlbCBhbnl3YXkgYmMgb2YgbGVmdF9qb2luIGJlbG93LgogIHJvd25hbWVzX3RvX2NvbHVtbigiY2x1c3Rlcl9sYWJlbCIpCmBgYAoKIyMjIyA1LiBgY2wuY2VudGVyLmRmYApKb2luIGBjbC5kZmAgYW5kIGByZC5jbC5jZW50ZXJgIGludG8gYGNsLmNlbnRlci5kZmAgZm9yIGlucHV0IGludG8gYGdldF9LTk5fZ3JhcGhgLgpgYGB7cn0KY2wuY2VudGVyLmRmIDwtIGxlZnRfam9pbihyZC5jbC5jZW50ZXIsIGNsLmRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImNsdXN0ZXJfbGFiZWwiKSkgCmBgYAoKIyMjIyA2LiBga25uLmNsYCAKYGBge3J9CiMgRml4ZXMgbmVlZGVkIGZvciBwcm9wZXIgb3V0cHV0IG9mIGtubi5jbC5kZgojIGNsLmNlbnRlci5kZiAlPD4lIHJlbmFtZShjbHVzdGVyX3NpemUgPSAic2l6ZSIpCiMgbGV2ZWxzKGNsKSA8LSBjbC5kZiRjbHVzdGVyX2lkCmNsLm51bWVyaWMgPC0gY2wKbGV2ZWxzKGNsLm51bWVyaWMpIDwtIGNsLmRmJGNsdXN0ZXJfaWQKCmtubi5yZXN1bHQgPC0gUkFOTjo6bm4yKGRhdGEgPSByZC5kYXQkcGNhWywgMToxMF0sIGsgPSAxNSkKCmtubi5jbCA8LSBnZXRfa25uX2dyYXBoKHJkLmRhdCA9IHJkLmRhdCRwY2FbICwgMToxMF0sIAogICAgICAgICAgICAgICAgICAgICAgICBjbC5kZiA9ICBjbC5kZiwgY2wgPSBjbC5udW1lcmljLCAKICAgICAgICAgICAgICAgICAgICAgICAgayA9IDE1LCAKICAgICAgICAgICAgICAgICAgICAgICAga25uLm91dGxpZXIudGggPSAyLCBvdXRsaWVyLmZyYWMudCA9IDAuNSkKCnJtKHJkLmRhdCwgbmN4LmNsdXN0ZXJzKQpgYGAKCiMjIDMuIE1ha2UgY29uc3RlbGxhdGlvbiBwbG90CmBgYHtyIG1ha2UtY29uc3RlbGxhdGlvbiwgZXZhbCA9IEZBTFNFfQojIEtlZXAgb25seSBjZWxscyB3aGVyZSAkZnJhYyBbZnJhY3Rpb24gb2YgY2VsbHMgaW4gY2x1c3RlciB3aXRoIG5lYXJlc3QgbmVpZ2hib3JzIGluIGRpZmZlcmVudCBjbHVzdGVyXSA+PSAwLjA1LgojIERlZmluZWQgaW4gYGdldF9rbm5fZ3JhcGhgOiAKIyBrbm4uY2wuZGYkZnJhYyA9IGtubi5jbC5kZiRGcmVxIC8ga25uLmNsLmRmJGNsLmZyb20udG90YWwKIyAxMCUgOiAyMTMgZWRnZXMKa25uLmNsLmRmIDwtIGtubi5jbCRrbm4uY2wuZGYgICU+JSBmaWx0ZXIoZnJhYyA+PSAwLjEpCgojIFBsb3Qgb25seSBlZGdlcyBiZXR3ZWVuIEV4TiBsaW5lYWdlIGNsdXN0ZXJzLgojIGtubi5jbC5kZiAlPD4lIGZpbHRlcl9hdCh2YXJzKGNsLmZyb20ubGFiZWwsIGNsLnRvLmxhYmVsKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBhbGxfdmFycyhzdHJfZGV0ZWN0KC4sICJSR3xJUEN8TmV1cm9ufE9QQ3xEaXZpZGluZyIpKQojICAgICAgICAgICAgICApCgpjbC5jZW50ZXIuZGYkY2x1c3Rlcl9sYWJlbCAlPD4lIHN0cl9yZW1vdmUoIl9jb21ibzIiKQogIApjbC5wbG90IDwtIHBsb3RfY29uc3RlbGxhdGlvbihrbm4uY2wuZGYsIGNsLmNlbnRlci5kZiwgb3V0LmRpciA9ICIuLi9vdXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlLmxhYmVsID0gImNsdXN0ZXJfbGFiZWwiLCBleHhhZ2VyYXRpb24gPSAxLCBjdXJ2ZWQgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5wYXJ0cyA9IEZBTFNFLCBwbG90Lmh1bGwgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5oZWlnaHQgPSA0MCwgcGxvdC53aWR0aCA9IDQwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub2RlLmRvZGdlID0gVFJVRSwgbGFiZWwuc2l6ZSA9IDIsIG1heF9zaXplID0gMjApCgpgYGAKCioqKgojIDIuIEZpbmQgREUgZ2VuZXMgYmV0d2VlbiBjb25uZWN0ZWQgY2x1c3RlcnMuCioqKgpgYGB7cn0KIyBEYXRhZnJhbWUgdy8gdGhlIHByb3BvcnRpb24gb2YgaygxNSkgbmVhcmVzdCBuZWlnaGJvcnMgaW4gZWFjaCBjbHVzdGVyIGZvciBldmVyeSBjZWxsLgpubi5jbC5kZiA8LSBrbm4uY2wkcHJlZC5yZXN1bHQkcHJlZC5wcm9iICU+JSBhcy5kYXRhLmZyYW1lCgpjbC5kZiRjbHVzdGVyX2lkICU8PiUgYXMuZmFjdG9yKCkKIyBQb3NzaWJseSBtb3ZlIHRvIHRvcCwgYmVmb3JlIG1ha2luZyBhbGwgREZzLgpjZWxscy5jbC5kZiAlPD4lIHJlbmFtZShjbHVzdGVyX2xhYmVsID0gImNvbWJpbmVkLmNsdXN0ZXIuMiIpICU+JSAKICAgICAgICAgICAgICAgICAgbXV0YXRlKGNsdXN0ZXJfbGFiZWwgPSBzdHJfcmVtb3ZlKGNsdXN0ZXJfbGFiZWwsICJfY29tYm8yIikpCgpjbC5kZiAlPD4lIG11dGF0ZShjbHVzdGVyX2xhYmVsID0gc3RyX3JlbW92ZShjbHVzdGVyX2xhYmVsLCAiX2NvbWJvMiIpKQoKCiMgQWRkIGNvbHVtbiB3aXRoIGNlbGxzJyBvd24gY2x1c3RlciBhc3NpZ25tZW50IGZyb20gYGNlbGxzLmNsLmRmYC4Kbm4uY2wuZGYgJTw+JSBsZWZ0X2pvaW4oY2VsbHMuY2wuZGYsCiAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygicXVlcnkiID0gImNlbGwubmFtZSIpCiAgICAgICAgICAgICAgICAgICAgICAgICkKIyBBZGQgY2x1c3Rlcl9sYWJlbCBjb3JyZXNwb25kaW5nIHRvIG5uLmNsLgoKbm4uY2wuZGYgJTw+JSBsZWZ0X2pvaW4oY2wuZGYgJT4lIHNlbGVjdChjbHVzdGVyX2xhYmVsLCBjbHVzdGVyX2lkKSwKICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJubi5jbCIgPSAiY2x1c3Rlcl9pZCIpKQoKbm4uY2wuZGYgJTw+JSBzZWxlY3QocXVlcnksIGNsdXN0ZXJfaWRfc2VsZiA9ICJjbHVzdGVyX2lkIiwgCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9sYWJlbF9zZWxmID0gImNsdXN0ZXJfbGFiZWwueCIsCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9pZF9ubiA9ICJubi5jbCIsCiAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9sYWJlbF9ubiA9ICJjbHVzdGVyX2xhYmVsLnkiLAogICAgICAgICAgICAgICAgICAgIGZyZXEgPSAiRnJlcSIpCgojIG5uLmNsLmRmICU8PiUgc2VsZWN0KHF1ZXJ5LCBjb21iaW5lZC5jbHVzdGVyLjIsIGNsdXN0ZXJfaWQsIG5uLmNsLCApCgpgYGAKCmBgYHtyfQp4IDwtIGZpbHRlcihrbm4uY2wka25uLmNsLmRmLCBmcmFjID49IDAuMSAmIGNsLmZyb20gIT0gY2wudG8pICU+JSBhcnJhbmdlKGNsLmZyb20pCmBgYAoKYGBge3J9Cmtubi5jbCRrbm4uY2wuY2wuY291bnRzICU+JSBoZWFkCmBgYAoKYGBge3J9CmNsLmRmCgp4IDwtIGZpbHRlcihubi5jbC5kZiwgY2x1c3Rlcl9sYWJlbF9zZWxmID09ICJOZXVyb25fOCIgJiBjbHVzdGVyX2xhYmVsX25uID09ICJOZXVyb25fMyIpCiMgMTIsMjAxIGNlbGxzIGluIE5ldXJvbl84Cgp4ICU+JSBmaWx0ZXIoZnJlcSA+IDApICU+JSAKICBnZ3Bsb3QoKSArIGdlb21fZGVuc2l0eShhZXMoZnJlcSkpCmBgYAoKTmV1cm9uXzMgY2VsbHMgd2l0aCBuZWFyZXN0IG5laWdoYm9ycyBpbiBOZXVyb25fOApgY2VsbC5jbC5jb3VudHNgIGlzIGEgbWF0cml4IG9mIGFsbCBjZWxscyBhbmQgdGhlaXIgY291bnRzIG9mIG5lYXJlc3QgbmVpZ2hib3JzIGluIGVhY2ggY2x1c3Rlci4KCmBgYHtyfQpjZWxsLmNsLmNvdW50cyA8LSBrbm4uY2wka25uLmNlbGwuY2wuY291bnRzICU+JSBhcy5kYXRhLmZyYW1lLm1hdHJpeCAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsLm5hbWUiKQoKY2VsbC5jbC5jb3VudHMgPC0gbGVmdF9qb2luKGNlbGxzLmNsLmRmLCBjZWxsLmNsLmNvdW50cywgIGJ5ID0gImNlbGwubmFtZSIpICU+JQogIHNlbGVjdCgtY2VsbC50eXBlKSAlPiUgbXV0YXRlKGNvbWJpbmVkLmNsdXN0ZXIuMiA9IHN0cl9yZW1vdmUoY29tYmluZWQuY2x1c3Rlci4yLCAiX2NvbWJvMiIpKSAlPiUKICByZW5hbWUoY2x1c3Rlcl9sYWJlbCA9ICJjb21iaW5lZC5jbHVzdGVyLjIiKQoKCm5hbWVzKGNlbGwuY2wuY291bnRzKVs0Om5yb3coY2VsbC5jbC5jb3VudHMpXSAlPD4lIHBhc3RlMCgiY2xfIiwgLikKYGBgCgojIEZpbmQgY2VsbHMgaW4gYGNsdXN0ZXJfYWAgd2l0aCBiZWFyZXN0IG5laWdoYm9ycyBpbiBgY2x1c3Rlcl9iYApgYGB7ciBlY2hvPVRSVUV9CmNsdXN0ZXJfYSA8LSAiSVBDXzYiCmNsdXN0ZXJfYiA8LSAiSVBDXzMiCgpjbHVzdGVyX2JfY29sIDwtIGNlbGwuY2wuY291bnRzICU+JSBmaWx0ZXIoY2x1c3Rlcl9sYWJlbCA9PSBjbHVzdGVyX2IpICU+JSAKICAgICAgICAgICAgICAgIC4kY2x1c3Rlcl9pZCAlPiUgYXMubnVtZXJpYyAlPiUgdW5pcXVlICU+JSBwYXN0ZTAoImNsXyIsIC4pCgoKeCA8LSBjZWxsLmNsLmNvdW50cyAlPiUgZmlsdGVyKGNsdXN0ZXJfbGFiZWwgPT0gY2x1c3Rlcl9hKSAlPiUgcmVwbGFjZShpcy5uYSguKSwgMCkKCm5uLmNvdW50cyA8LSB4W1tjbHVzdGVyX2JfY29sXV0Kbi5jZWxscyA8LSBsZW5ndGgoeCRjZWxsLm5hbWUpCnN1bShubi5jb3VudHMgPiAwKQpzdW0obm4uY291bnRzID09IDApCgoobWVkaWFuLm5uQ291bnRzIDwtIG1lZGlhbihubi5jb3VudHNbbm4uY291bnRzID4gMF0pKQpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBuZWlnaGJvciBjb3VudHMgaW4gY2x1c3Rlcl9iIGZvciBjZWxscyBpbiBhIGdpdmVuIGNsdXN0ZXJfYS4KVXNlIHRoaXMgdG8gZmluZCB0aGUgcG9pbnQgYXQgd2hpY2ggY2VsbHMgd2lsbCBiZSBzcGxpdCBpbnRvIGNvbXBhcmlzb24gZ3JvdXBzLgpgYGB7cn0KICBnZ3Bsb3QoeCkgKyAKICAgIGdndGl0bGUocGFzdGUoY2x1c3Rlcl9hLCAiY2VsbHMgXG4gbi9rPTE1IG5lYXJlc3QgbmVpZ2hib3JzIGluIiwgY2x1c3Rlcl9iKSkgKwogICAgCiAgICBnZW9tX2JhcihhZXMoeCA9IGdldChjbHVzdGVyX2JfY29sKSkpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lZGlhbi5ubkNvdW50cywgY29sb3VyID0gInJlZCIpICsKICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IG1lZGlhbi5ubkNvdW50cyArIDEsIHkgPSBxdWFudGlsZSgxOiBuLmNlbGxzLCAuMDcpLAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZTAoIm1lZGlhbiA9ICIsIG1lZGlhbi5ubkNvdW50cykKICApICsgCiAgICB4bGFiKHBhc3RlKCIjIG9mIiwgY2x1c3Rlcl9iLCAibmVpZ2hib3JzIikpICsKICAgIHlsYWIoIiIpICsKICAgIHRoZW1lX21pbmltYWwoKSAKYGBgCgpDb21wYXJlIGNlbGxzIGFib3ZlIGFuZCBiZWxvdyB0aGUgbWVkaWFuIGNvdW50IG9mIGBjbHVzdGVyX2JgIG5laWdoYm9ycy4KYGBge3IgZWNobz1UUlVFfQpjZWxscyA8LSBsaXN0KCkKY2VsbHMkYWJvdmUubWVkaWFuIDwtIHggJT4lIGZpbHRlcihnZXQoY2x1c3Rlcl9iX2NvbCkgPiBtZWRpYW4pICU+JSBwdWxsKGNlbGwubmFtZSkKY2VsbHMkYmVsb3cubWVkaWFuIDwtIHggJT4lIGZpbHRlcihnZXQoY2x1c3Rlcl9iX2NvbCkgPCBtZWRpYW4pICAlPiUgcHVsbChjZWxsLm5hbWUpCgpzLm9iaiA8LSBTZXRJZGVudChzLm9iaiwgY2VsbHMgPSBjZWxscyRhYm92ZS5tZWRpYW4sIHZhbHVlID0gJ25uX2N0X2Fib3ZlX21lZCcpICU+JSAKICAgICAgICAgIFNldElkZW50KGNlbGxzID0gY2VsbHMkYmVsb3cubWVkaWFuLCB2YWx1ZSA9ICdubl9jdF9iZWxvd19tZWQnKQoKdGFibGUocy5vYmpAYWN0aXZlLmlkZW50KSAlPiUgYXMuZGF0YS5mcmFtZS5tYXRyaXgoKQptYXJrZXJzIDwtIGxpc3QoKQptYXJrZXJzJG5uX2Fib3ZlX21lZCA8LSBGaW5kTWFya2VycyhzLm9iaiwgc2xvdCA9ICJzY2FsZS5kYXRhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNlbGxzLjEgPSBjZWxscyRhYm92ZS5tZWRpYW4sIGNlbGxzLjIgPSBjZWxscyRiZWxvdy5tZWRpYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4xID0gIm5uX2Fib3ZlX21lZCIsIGlkZW50LjIgPSAibm5fYmVsb3dfbWVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAwKQpgYGAKCkNhbGN1bGF0ZSBlbnJpY2htZW50IHJhdGlvLCBmaWx0ZXIgZ2VuZXMgdy4gYWRqIHAtdmFsdWUgPCAwLjA1LCBzb3J0IHRhYmxlLgpQb3NpdGl2ZSB2YWx1ZXMgaW5kaWNhdGUgdGhhdCB0aGUgZmVhdHVyZSBpcyBtb3JlIGhpZ2hseSBleHByZXNzZWQgaW4gdGhlIGZpcnN0IGdyb3VwLgpgYGB7ciBmaWx0ZXItbWFya2VycywgZWNobz1UUlVFfQoKbWFya2Vycy50bXAgPC0gbWFya2VycyRubl9hYm92ZV9tZWQgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZSIpICU+JQogIG11dGF0ZShlbnJpY2gucmF0aW8gPSBwY3QuMSAvIHBjdC4yLAogICAgICAgICBnZW5lLnNjb3JlID0gYXZnX2RpZmYgKiBlbnJpY2gucmF0aW8sCiAgICAgICAgIGFjcm9zcyguY29scyA9IHdoZXJlKGlzLm51bWVyaWMpLCAuZm5zID0gcm91bmQsIGRpZ2l0cyA9IDUpCiAgKSAlPiUKICBmaWx0ZXIocF92YWxfYWRqIDw9IDAuMDUpICU+JSAKICBzZWxlY3QoZ2VuZSwgcGN0LjEsIHBjdC4yLCBlbnJpY2gucmF0aW8sIGF2Z19kaWZmLCBnZW5lLnNjb3JlLCBwX3ZhbF9hZGopICU+JQogIGFycmFuZ2UoZGVzYyhnZW5lLnNjb3JlKSkKCndyaXRlX3RzdihtYXJrZXJzLnRtcCwgIi4uL291dC9ERWdlbmVzX25ldXJvbjNfdnNfbmV1cm9uOC50c3YiKQpgYGAKCmBgYHtyIHJlbmRlci10YWJsZX0KcmVhY3RhYmxlKG1hcmtlcnMudG1wLCBkZWZhdWx0UGFnZVNpemUgPSAxMDAsCiAgICAgICAgICBzaG93U29ydGFibGUgPSBUUlVFLCByZXNpemFibGUgPSBUUlVFLCBoaWdobGlnaHQgPSBUUlVFLCBmaWx0ZXJhYmxlID0gVFJVRSwgbWluUm93cyA9IDEwLAogICAgICAgICAgc3R5bGUgPSBsaXN0KGZvbnRGYW1pbHkgPSAiV29yayBTYW5zLCBzYW5zLXNlcmlmIiwgZm9udFNpemUgPSAiMTJweCIpCiAgKQpgYGAKCmBgYHtyfQojIHNhdmVXaWRnZXQobWFya2Vycy50bXAsIGZpbGUgPSApCgojIFNhbWUgZ2VuZXMgaW4gYm90aCBjb21wYXJpc29ucyAoc2FuaXR5IGNoZWNrKQp4dGFiX3NldCA8LSBmdW5jdGlvbihBLEIpewogICAgICAgICAgICAgIGJvdGggICAgPC0gIHVuaW9uKEEsQikKICAgICAgICAgICAgICBpbkEgICAgIDwtICBib3RoICVpbiUgQQogICAgICAgICAgICAgIGluQiAgICAgPC0gIGJvdGggJWluJSBCCiAgICAgICAgICAgICAgcmV0dXJuKHRhYmxlKGluQSxpbkIpKQogICAgICAgICAgICB9Cnh0YWJfc2V0KG1hcmtlcnMkbm5fYWJvdmVfbWVkJGdlbWUsIG1hcmtlcnMkbm5fYmVsb3dfbWVkJGdlbmUpCmBgYAoKTWFrZSBoZWF0bWFwOgpgYGB7ciBoZWF0bWFwfQp0bXAuc29iaiA8LSBzdWJzZXRTZXVyYXQocy5vYmosIGNlbGxzLmtlZXAgPSBmbGF0dGVuX2NocihjZWxscykpCgptaW4odG1wLnNvYmpAYXNzYXlzJFJOQUBzY2FsZS5kYXRhKQoKbWFya2Vycy5wbG90IDwtIG1hcmtlcnMudG1wICAlPiUgZmlsdGVyKGVucmljaC5yYXRpbyA+PSAxLjMgfCBlbnJpY2gucmF0aW8gPD0gMC43KSAlPiUgYXJyYW5nZShkZXNjKGVucmljaC5yYXRpbykpCgpleHAucGxvdCA8LSB0bXAuc29iakBhc3NheXMkUk5BQHNjYWxlLmRhdGFbbWFya2Vycy5wbG90JGdlbmUsIF0KCihleHAubGltaXRzIDwtIGV4cC5wbG90ICU+JSBhcy5udW1lcmljICU+JSAgcXVhbnRpbGUoYygwLCAwLjAxLCAwLjA1LCAwLjEsIDAuNSwgMC45LCAwLjk1LCAwLjk5LCAxKSkpCgpoZWF0bWFwIDwtIAogIERvSGVhdG1hcCh0bXAuc29iaiwgCiAgICAgICAgICAjIGNlbGxzID0gZmxhdHRlbl9jaHIoY2VsbHMpLAogICAgICAgICAgZmVhdHVyZXMgPSAgbWFya2Vycy5wbG90JGdlbmUsCiAgICAgICAgICBkaXNwLm1pbiA9IGV4cC5saW1pdHNbIjElIl0sCiAgICAgICAgICBkaXNwLm1heCA9IGV4cC5saW1pdHNbIjk5JSJdLAogICAgICAgICAgYW5nbGUgPSAwCiAgICAgICAgICAjIHNsb3QgPSAiZGF0YSIKICAgICAgICAgICMgc2xpbS5jb2wubGFiZWwgPSBUUlVFLAogICAgICAgICAgIyByZW1vdmUua2V5ID0gVFJVRQopICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGFzcGVjdC5yYXRpbyA9IGMoMiwxKQogICkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzKGVuZCA9IDEsIG5hLnZhbHVlID0gJ3doaXRlJywgb3B0aW9uID0gIm1hZ21hIiwgZGlzY3JldGUgPSBGQUxTRSkKCiMgZ2dzYXZlKGZpbGVuYW1lID0gIi4uL0RFZ2VuZXNfbmV1cm9uM192c19uZXVyb244X2hlYXRtYXAucGRmIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIgKQoKcCA8LSBnZ3Bsb3RseShoZWF0bWFwLCB0b29sdGlwID0gIkZlYXR1cmUiLCB3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDgwMCkgJT4lIGxheW91dChsZWdlbmQgPSBsaXN0KHlhbmNob3IgPSAnYm90dG9tJywgb3JpZW50YXRpb24gPSAnaCcpKSAlPiUgcGFydGlhbF9idW5kbGUoKSAKCnBsb3RseV9qc29uKHApCnAkeCRkYXRhW2MoMiwzKV0gPC0gTlVMTAoKICBzYXZlV2lkZ2V0KCJ+L2Nhcm1lbnNhbmRvdmFsLmdpdGh1Yi5pby9hcmtsYWIvMm5kLXRyaS9jbHVzdGVyaW5nL0RFZ2VuZXNfbmV1cm9uM192c19uZXVyb244X2hlYXRtYXAuaHRtbCIsIHNlbGZjb250YWluZWQgPSBUUlVFKQpgYGAKCkV4dHJhIGZ1bmN0aW9ucwpgYGB7ciBmdW5jdGlvbnN9Ci5lbnYkc291cmNlX3JtZCA8LSBmdW5jdGlvbihmaWxlLCBsb2NhbCA9IEZBTFNFLCAuLi4pewogIG9wdGlvbnMoa25pdHIuZHVwbGljYXRlLmxhYmVsID0gJ2FsbG93JykKCiAgdGVtcFIgPC0gdGVtcGZpbGUodG1wZGlyID0gIi4iLCBmaWxlZXh0ID0gIi5SIikKICBvbi5leGl0KHVubGluayh0ZW1wUikpCiAga25pdHI6OnB1cmwoZmlsZSwgb3V0cHV0ID0gdGVtcFIsIHF1aWV0ID0gVFJVRSkKCiAgZW52aXIgPC0gZ2xvYmFsZW52KCkKICBzb3VyY2UodGVtcFIsIGxvY2FsID0gZW52aXIsIC4uLikKfQoKLmVudiRyZWFjdGFibGUgPC0gZnVuY3Rpb24oLi4uKSB7CiAgaHRtbHRvb2xzOjp0YWdMaXN0KHJlYWN0YWJsZTo6cmVhY3RhYmxlKC4uLikpCn0KCiMgc291cmNlKCIuLi8uLi8uLi9jb2RlX2dlbmVyYWwvc2V0dXBfUl9zZXNzaW9uX0NTRS5SIikKCmF0dGFjaCguZW52KQpgYGA=